13. 正则表达式(RegularExpression, re)

- 是一个计算机科学的概念
- 用于使用单个字符串来描述,匹配一系列匹配某个句法规则的字符串
- 常用来检索,替换某些模式的文本
# 正则表达是的写法
- .(点号): 匹配任意一个字符,出了 \n
- []:匹配中括号中列举的字符,例如[LY]
- \d: 表示任意一个数字, 0-9
- \D: 表示非数字,即不是数字就可以
- \s:匹配空白,即空格,tab键
- \S:除了空白意外的字符
- \w:单词字符,即a-z, A-Z, 0-9, _
- \W:非单词字符
- *: 表示前面内容重复零次或者多次
- +:前面内容重复至少一次
- ?:前面出现的内容零次或者一次
- {m,n}:允许前面内容出现最少m次,最多n次
- ^:匹配字符串的开始
- $:匹配字符串的结尾
- \b:匹配单词的边界
- ():对正则表达式内容进行分组, 从第一个括号开始,编号逐渐增大
    
        验证一个数字: ^\d$
        必须有一个数字,最少一位:^\d+$
        只能出现数字,且位数为5-10位: ^\d{5,10}$
        注册者输入年龄,要求16岁以上,99岁以下: ^[16-99]$
        只能输入英文字符和数字: ^[A-Za-z0-9]$
        验证qq号码: [0-9]{5,12}
- \A: 只匹配字符串开头, \Aabcd, 则abcd
- \Z: 仅匹配字符串末尾, abcd\Z, abcd
- |: 左右任意一个
- (?P<name>...): 分组,除了原来的编号再制定一个别名, (?P<id>12345){2}, 1234512345
- (?P=name): 引用分组, 
        
# re 模块
- re.match(re_exp):从开始查看是否能跟定义的正则相匹配
- result.group:如果匹配数据成功,可以使用group来提取数据
- 案例01

# re中原生字符串问题
- 字符串中会包含转义字符
- 在re中,转义字符会带来一些麻烦,能不能尽量减少转义字符的出现
- 在字符串前使用r字符,表示后面字符串是原始字符串,不需要转移,例如 r'c:\wk\readme.txt'
- 案例01-fun_4
# 正则的分组表示
- |:匹配左右任意一个表达式
- (ab):将括号中字符作为一个分组
- \num:引用分组num匹配到的字符串
- (?P<name>):分组起一个别名, 其中P是大写
- (?P=num):引用别名为name分组匹配到的字符串
- 案例01中 fun_5函数

# re 模块的一般使用步骤如下:
1. 使用 compile() 函数将正则表达式的字符串形式编译为一个 Pattern 对象 
2. 通过 Pattern 对象提供的一系列方法对文本进行匹配查找,获得匹配结果,一个 Match 对象。
3. 最后使用 Match 对象提供的属性和方法获得信息,根据需要进行其他的操作 

# re常用函数
- group([group1, …]) 方法用于获得一个或多个分组匹配的字符串,当要获得整个匹配的子串时,可 直接使用 group() 或 group(0); 
- start([group]) 方法用于获取分组匹配的子串在整个字符串中的起始位置(子串第一个字符的索 引),参数默认值为 0; 
- end([group]) 方法用于获取分组匹配的子串在整个字符串中的结束位置(子串最后一个字符的索引 +1),参数默认值为 0; 
- span([group]) 方法返回 (start(group), end(group))。

# 正则的其他用法
- 案例01, fun_6
- search:查找其中一个内容
- findall:查找所有内容
- sub:查找替换, 返回替换之后的内容,原来倍替换的内容不变

# 匹配代码

        import re

        p = re.compile(r'\d+') 

        m = p.match("one12twothree34four")

        print(m)

        import re
         

        m = p.match("one12twothree34four", 3, 10)

        print(m)

        print(m[0])

        m.start(0) # 参数是组号
        m.end(0)

        #第二个例子
        import re

        p = re.compile(r'([a-z]+) ([a-z]+)',re.I) # re.I表示忽略大小写, 两个中间有空格

        m = p.match('I really love wangxiaojing ')

        print(m)

        print(m.group(0))

        m.start(0)

        m.span(0)

        m.group(1)

        m.group(2)

        m.group(0) # 整个匹配子串

        m.groups() # 全部的打印出来


# 查找代码
- search(str[, pos[, endpos]])
- findall
- finditer
        
       import re
       p = re.compile(r'\d+')

       m = p.search('one12twothree34four')

       m.group()

       p = re.compile('\d+')

       m = p.search('hello 123456 7890')

       print(m.group())
       
       
       #findall
       rst = p.findall("hello 12345 67890")
       print(rst)


# sub替换
- sub(rep1, str[,count])

    
        import re
         
        p = re.compile(r'(\w+) (\w+)') #\w是字符
        s = "hello 123 and hello 456"

        rst = p.sub(r'hello world', s)
        print(rst)

        rst = p.sub(r'\2 \1', s)
        print(rst)

        def func(m):
            return 'gaga' + m.group(2)

        rst = p.sub(func, s)
        print(rst)

        rst = p.sub(func, s,1)
        print(rst)
        
# 匹配中文
- 中文主要表示范围是 [u4e00-u9fa5], 不包括拳脚标点

        import re

        title = u'你好 世界, hello moto'

        p = re.compile(ur"[\u4e00-\u9fa5]+") # ur有可能冲突

        rst = p.findall(title)

        print(rst)
        
# 贪婪和非贪婪
- 尽可能多的匹配,(*)
- 非贪婪, (?)
- 默认贪婪

        import re

        title = u'<div>name</div><div>age</div>'


        p1 = re.compile(r"<div>.*</div>")  
        p2 = re.compile(r"<div>.*?</div>")  

        m1 = p1.search(title)
        print(m1.group())

        m2 = p2.search(title)
        print(m2.group())